<?php

/**
 * Base class for users and clients
 * @package Nina
 * @author John Main <john@thedaddy.org>
 */
class Nina_User_Base implements Nina_User_Account
{

	protected $type;
	protected $type_class;
	protected $id = 0;
	protected $password = '';
	protected $reg;
	protected $cookie_session_name;
	protected $cookie_session_timeout;
	protected $obj;
	protected $login_type;
	protected $message;

	/**
	 * Constructor
	 * @param boolean [$user=true] Is this a user record?
	 */
	public function __construct($user = true) {
		$this->reg = Nina_Registry::getInstance();

		if (!$this->reg->exists('security.encryption')) {
			$this->reg->set('security.encryption', 'whirlpool');
		}

		$this->type = ($user) ? 'user' : 'client';
		$this->extractCookieSession();
		$this->type_class = ucfirst($this->type);
		$this->message = new Nina_Message();

		if($this->id && $this->password) {

			/*
			$type_class = $this->type_class;

			if($this->id && $this->password) {
				$c = User::getSpecialConditionsCriteria();
				$c->add($type_class::ID, $this->id);
				$c->add($type_class::PASSWORD, $this->password);
				if($instance = $type_class::doSelectOne(new $type_class(), $c)) {
					$this->obj = $instance;
					$this->setCookieSession();
				}
				else
					$this->logout();
			}
			*/

			if($user) {
				$c = User::getSpecialConditionsCriteria();
				$c->add(User::ID, $this->id);
				$c->add(User::PASSWORD, $this->password);
				if($instance = User::doSelectOne(new User(), $c)) {
					$this->obj = $instance;
					$this->setCookieSession();
				}
				else
					$this->logout();
			}
			else {
				$c = Client::getSpecialConditionsCriteria();
				$c->add(Client::ID, $this->id);
				$c->add(Client::PASSWORD, $this->password);
				if($instance = Client::doSelectOne(new Client(), $c)) {
					$this->obj = $instance;
					$this->setCookieSession();
				}
				else
					$this->logout();
			}
		}
	}

	/**
	 * Get the current login details
	 */
	protected function extractCookieSession() {

		if (!$this->reg->get("login.type")) {
			$this->reg->set("login.type", 'session');
		}
		$this->login_type = strtolower($this->reg->get("login.type"));

		if (!$this->reg->get('login.'.$this->type.'.name')) {
			$this->reg->set('login.'.$this->type.'.name', 'nina_user');
		}
		$this->cookie_session_name = $this->reg->get('login.'.$this->type.'.name');

		if (!$this->reg->get('login.'.$this->type.'.timeout')) {
			$this->reg->set('login.'.$this->type.'.timeout', 3600);
		}
		$this->cookie_session_timeout = $this->reg->get('login.'.$this->type.'.timeout');

		if($this->isCookie()) {

			if(isset($_COOKIE[$this->cookie_session_name]))
				list($this->id, $this->password) = explode('-', $_COOKIE[$this->cookie_session_name]);
		}
		else {
			session_name($this->cookie_session_name);
			session_set_cookie_params($this->cookie_session_timeout);

			if(!session_id())
				session_start();

			if($_SESSION && isset($_SESSION[$this->cookie_session_name]) && isset($_SESSION[$this->cookie_session_name]['id'])) {
				$this->id = $_SESSION[$this->cookie_session_name]['id'];
				$this->password = $_SESSION[$this->cookie_session_name]['password'];
			}
		}
	}

	/**
	 * Set the cookie or session information
	 */
	protected function setCookieSession() {
		if($this->isCookie())
			setcookie($this->cookie_session_name, $this->id.'-'.$this->password, time()+$this->cookie_session_timeout, '/');
		else
			$_SESSION[$this->cookie_session_name] = array('id' => $this->id, 'password' => $this->password);

		$this->reg->set('runtime.user', $this->obj);
	}

	/**
	 * Clear the cookie or session information
	 */
	protected function clearCookieSession() {
		if($this->isCookie())
			setcookie($this->cookie_session_name, '', time()-1, '/');

		session_destroy();

		$this->reg->delete('runtime.user');
	}

	/**
	 * Login
	 * @param string $username
	 * @param string $password
	 * @return boolean|integer
	 */
	public function login($username, $password) {

		if (!$this->reg->get('security.client-side-encryption')) {
			$password = self::encode($password);
		}

		if($username && $password) {
			if($this->type == 'user') {
				$c = User::getSpecialConditionsCriteria();
				$c->add(User::USERNAME, $username);
				$obj = User::doSelectOne(new User(), $c);
			}
			else {
				$c = Client::getSpecialConditionsCriteria();
				$c->add(Client::USERNAME, $username);
				$obj = Client::doSelectOne(new Client(), $c);
			}
		}

		if(isset($obj) && $obj) {

			if($obj->getPassword() == $password) {
				if($obj->getActive()) {
					$this->obj = $obj;
					$this->id = $obj->getId();
					$this->password = $obj->getPassword();
					$this->setCookieSession();

					if(defined(ucfirst($this->type) . '::LAST_LOGIN'))
						$obj->setLastLogin(Nina_Timestamp::now());

					$obj->setLoginAttempts(0);
					$obj->save();

					return true;
				}
				else
					$this->message->add('Your account is disabled. Please contact your administrator.');
			}
			else {
				$obj->setLoginAttempts($obj->getLoginAttempts()+1);

				if($obj->getLoginAttempts() >= Setting::get('max_login_attempts')) {
					$this->message->add('You have had '.$obj->getLoginAttempts().' login attempts and your account is now disabled. Please contact your administrator');

					$obj->setActive(0);
				}
				else
					$this->message->add('Your username / password was not recognised');

				$obj->save();
			}
		}
		else
			$this->message->add('Your username was not recognised');

		return false;
	}

	/**
	 * Login By ID
	 * @param integer $id ID
	 * @return boolean|integer
	 */
	public function loginById($id) {
		if($this->type == 'user')
			$obj = User::retrieveByPk(new User(), $id);
		else
			$obj = Client::retrieveByPk(new Client(), $id);

		if($obj) {
			if($obj->getActive()) {
				$this->obj = $obj;
				$this->id = $obj->getId();
				$this->password = $obj->getPassword();
				$this->setCookieSession();

				if(defined('User::LAST_LOGIN'))
					$obj->setLastLogin(Nina_Timestamp::now());

				$obj->setLoginAttempts(0);
				$obj->save();

				return true;
			}
			else
				$this->message->add('Account disabled');
		}
		else
			$this->message->add('Not recognised');

		return false;
	}

	/**
	 * Logout
	 */
	public function logout() {

		//$this->obj = new $this->type_class();
		$this->obj = ($this->type == 'user') ? new User() : new Client();

		$this->id = 0;
		$this->password = '';
		$this->clearCookieSession();
	}

	/**
	 * Change the password
	 * @param string $password
	 * @param return $boolean
	 */
	public function setPassword($password) {
		if($password) {

			if (!$this->reg->get('security.client-side-encryption'))
				$password = self::encode($password);

			$this->password = $password;
			$this->obj->setPassword($password);
			$this->obj->save();
			$this->setCookieSession();
			return true;
		}

		return false;
	}

	/**
	 * Check if we are using cookies for authentication
	 * @return boolean
	 */
	public function isCookie() {
		return ($this->login_type == 'cookie');
	}

	/**
	 * Check if we are using sessions for authentication
	 * @return boolean
	 */
	public function isSession() {
		return ($this->login_type == 'session');
	}

	/**
	 * Get the ID.
	 * @return integer
	 */
	public function getId() {
		return $this->id;
	}

	/**
	 * Check if the user/clent is logged in.
	 * @return boolean
	 */
	public function isGuest() {
		return ($this->id == 0);
	}

	/**
	 * Return the db model
	 * @return User
	 */
	public function getObjModel() {
		return $this->obj;
	}

	/**
	 * Any unknown method calls will want to be run on the db model
	 * @param string $function The function name
	 * @param array $args The arguments
	 */
	public function __call($function, $args) {
		if($this->obj) {
			return call_user_func_array(array($this->obj, $function), $args);
		}
		else
			return false;
	}

	/**
	 * Encode a password with the defined encryption method
	 * @param string $password The password to encrypt
	 * @return string
	 */
	public static function encode($password) {
		$reg = Nina_Registry::getInstance();
		$encryption = $reg->get('security.encryption');

		return hash($encryption, $password);
	}
}
